/*
 * TODO: remove and replace this file header comment
 * This is a .cpp file you will edit and turn in.
 * Remove starter comments and add your own
 * comments on each function and on complex code sections.
 */
#include "testing/SimpleTest.h"
#include "strlib.h"
#include "filelib.h"
#include "simpio.h"
#include <fstream>
#include <cctype>
#include <string>
#include "vector.h"
using namespace std;

/* This function is intended to return a string which
 * contains only the letter characters from the original
 * (all non-letter characters are removed)
 *
 * WARNING: The provided code is buggy!
 *
 * Use unit tests to identify which inputs to this function
 * are incorrectly handled. Then, remove this comment and
 * replace it with a description of the bug you fixed.
 */
string removeNonLetters(string s) {
    string result;
    for (int i = 0; i < s.length(); i++) {
        if (isalpha(s[i])) {
            result += s[i];
        }
    }
    return result;
}

string mergeDuplicates(string s) {
    string result;
    for (auto ch:s) {
        if (result.empty() || ch != result.back()) {
            result.push_back(ch);
        } else {
            result.pop_back();
            result.push_back(ch);
        }
    }
    return result;
}

/* TODO: Replace this comment with a descriptive function
 * header comment.
 */
string soundex(string s) {
    /* TODO: Fill in this function. */
    string letters1 = "AEIOUHWY";
    string letters2 = "BFPV";
    string letters3 = "CGJKQSXZ";
    string letters4 = "DT";
    string letters5 = "L";
    string letters6 = "MN";
    string letters7 = "R";
    string result = toUpperCase(removeNonLetters(s));
    char first_letter = result[0];
    for (int i = 0; i < s.length(); i++) {
        if (letters1.find(result[i]) != string::npos) {
            result[i] = '0';
        } else if (letters2.find(result[i]) != string::npos) {
            result[i] = '1';
        } else if (letters3.find(result[i]) != string::npos) {
            result[i] = '2';
        } else if (letters4.find(result[i]) != string::npos) {
            result[i] = '3';
        } else if (letters5.find(result[i]) != string::npos) {
            result[i] = '4';
        } else if (letters6.find(result[i]) != string::npos) {
            result[i] = '5';
        } else if (letters7.find(result[i]) != string::npos) {
            result[i] = '6';
        }
    }
    result = mergeDuplicates(result);
    result[0] = first_letter;
    result.erase(std::remove(result.begin(), result.end(), '0'),
               result.end());
    result.resize(4, '0');
    return result;
}


/* TODO: Replace this comment with a descriptive function
 * header comment.
 */
void soundexSearch(string filepath) {
    // The proivded code opens the file with the given name
    // and then reads the lines of that file into a vector.
    ifstream in;
    Vector<string> databaseNames;

    if (openFile(in, filepath)) {
        readEntireFile(in, databaseNames);
    }
    cout << "Read file " << filepath << ", "
         << databaseNames.size() << " names found." << endl;

    // The names in the database are now stored in the provided
    // vector named databaseNames

    /* TODO: Fill in the remainder of this function. */
    while (1) {
        string surname = getLine("Enter a surname (RETURN to quit): ");
        if (surname.empty()) {
            break;
        }
        string code = soundex(surname);
        cout << "Soundex code is " << code << endl;
        Vector<string> vec;
        for (auto name : databaseNames) {
            if (soundex(name) == code) {
                vec.add(name);
            }
        }
        vec.sort();
        cout << "Matches from database: " << vec << endl;
        vec.clear();
        cout << endl;
    }
}


/* * * * * * Test Cases * * * * * */


PROVIDED_TEST("Test removing puntuation, digits, and spaces") {
    string s = "O'Hara";
    string result = removeNonLetters(s);
    EXPECT_EQUAL(result, "OHara");
    s = "Planet9";
    result = removeNonLetters(s);
    EXPECT_EQUAL(result, "Planet");
    s = "tl dr";
    result = removeNonLetters(s);
    EXPECT_EQUAL(result, "tldr");
}


PROVIDED_TEST("Sample inputs from handout") {
    EXPECT_EQUAL(soundex("Curie"), "C600");
    EXPECT_EQUAL(soundex("O'Conner"), "O256");
}

PROVIDED_TEST("hanrahan is in lowercase") {
    EXPECT_EQUAL(soundex("hanrahan"), "H565");
}

PROVIDED_TEST("DRELL is in uppercase") {
    EXPECT_EQUAL(soundex("DRELL"), "D640");
}

PROVIDED_TEST("Liu has to be padded with zeros") {
    EXPECT_EQUAL(soundex("Liu"), "L000");
}

PROVIDED_TEST("Tessier-Lavigne has a hyphen") {
    EXPECT_EQUAL(soundex("Tessier-Lavigne"), "T264");
}

PROVIDED_TEST("Au consists of only vowels") {
    EXPECT_EQUAL(soundex("Au"), "A000");
}

PROVIDED_TEST("Egilsdottir is long and starts with a vowel") {
    EXPECT_EQUAL(soundex("Egilsdottir"), "E242");
}

PROVIDED_TEST("Jackson has three adjcaent duplicate codes") {
    EXPECT_EQUAL(soundex("Jackson"), "J250");
}

PROVIDED_TEST("Schwarz begins with a pair of duplicate codes") {
    EXPECT_EQUAL(soundex("Schwarz"), "S620");
}

PROVIDED_TEST("Van Niekerk has a space between repeated n's") {
    EXPECT_EQUAL(soundex("Van Niekerk"), "V526");
}

PROVIDED_TEST("Wharton begins with Wh") {
    EXPECT_EQUAL(soundex("Wharton"), "W635");
}

PROVIDED_TEST("Ashcraft is not a special case") {
    // Some versions of Soundex make special case for consecutive codes split by hw
    // We do not make this special case, just treat same as codes split by vowel
    EXPECT_EQUAL(soundex("Ashcraft"), "A226");
}

// TODO: add your test cases here
STUDENT_TEST("测试removeNonLetters函数，暴露其潜在的bug") {
    string s = "'Hara";
    string result = removeNonLetters(s);
    EXPECT_EQUAL(result, "Hara");
}

